/**
 * @description: eventBus
 * @author: huen2015
 */

import { IFn } from './types'

type FnList = IFn & {
	isOnce?: boolean
}

class EventBus {
	private queue: Map<string, FnList[]>
	constructor() {
		this.queue = new Map()
	}

	emit(channel: string, payload: any) {
		if (this.queue.has(channel)) {
			const fnList = this.queue.get(channel) as FnList[]
			fnList.forEach((fn) => {
				fn(payload)
				if (fn.isOnce) {
					this.off(channel, fn)
				}
			})
		}
	}

	on(channel: string, fn: FnList) {
		if (this.queue.has(channel)) {
			const fnList = this.queue.get(channel) as FnList[]
			fnList.push(fn)
			this.queue.set(channel, fnList)
		} else {
			const fnList: FnList[] = []
			fnList.push(fn)
			this.queue.set(channel, fnList)
		}
	}

	once(channel: string, fn: FnList) {
		if (this.queue.has(channel)) {
			const fnList = this.queue.get(channel) as FnList[]
			fn.isOnce = true
			fnList.push(fn)
			this.queue.set(channel, fnList)
		} else {
			const fnList: FnList[] = []
			fn.isOnce = true
			fnList.push(fn)
			this.queue.set(channel, fnList)
		}
	}

	off(channel?: string, fn?: FnList) {
		if (channel && fn) {
			if (this.queue.has(channel)) {
				const fnList = this.queue.get(channel) as FnList[]
				const _fnList = fnList.filter((fnItem) => fnItem !== fn)
				this.queue.set(channel, _fnList)
			}
		} else if (channel && !fn) {
			if (this.queue.has(channel)) {
				this.queue.delete(channel)
			}
		} else {
			this.queue.clear()
		}
	}
}

const eventBus = new EventBus()

const fn1 = (p: string) => {
	console.log('fn1 - ' + p)
}

const fn2 = (p: string) => {
	console.log('fn2 - ' + p)
}

const fn3 = (p: string) => {
	console.log('fn3 - ' + p)
}

const fn4 = (p: string) => {
	console.log('fn4 - ' + p)
}
eventBus.on('channel1', fn1)
eventBus.on('channel1', fn2)
eventBus.on('channel2', fn3)
eventBus.once('channel2', fn4)

eventBus.emit('channel1', 'channel1')
eventBus.emit('channel2', 'channel2')

console.log('once - channel2 - fn4')
eventBus.emit('channel2', 'channel2')

console.log('off - channel1 - fn1')
eventBus.off('channel1', fn1)
eventBus.emit('channel1', 'channel1')

console.log('off - all')
eventBus.off()
eventBus.emit('channel1', 'channel1')
eventBus.emit('channel2', 'channel2')
